# $Id$
# -*- coding: utf-8 -*-
import weakref
import events
import s_global

class entity(object):
	def __init__(self):
		self.owner = None
		self.aoi_list = []
		self.x_index = 0
		self.y_index = 0
		
class aoi_mgr(object):
	def __init__(self):
		self._all_entity = {}
		self.x_list = []
		self.y_list = []
			
	def get_aoi_list(self, pid):
		if pid not in self._all_entity:
			return None
		entity = self._all_entity[pid]
		return entity.aoi_list
			
	def add_sprite(self, sprite):
		if sprite.pid in self._all_entity:
			return
		new_entity = entity()
		new_entity.owner = weakref.proxy(sprite)
		
		x_idx, x_aoi = self.check_in_x(sprite)
		self.x_list.insert(x_idx, weakref.proxy(sprite))
		new_entity.x_index = x_idx
		
		y_idx, y_aoi = self.check_in_y(sprite)
		self.y_list.insert(y_idx, weakref.proxy(sprite))
		new_entity.y_index = y_idx
				
		#print "x_aoi", x_aoi
		#print "y_aoi", y_aoi
		for e in x_aoi:
			if e in y_aoi:
				new_entity.aoi_list.append(e)
				self.on_enter(sprite, e)
				self.on_enter(e, sprite)
		
		self._all_entity[sprite.pid] = new_entity
		return new_entity
		
	def del_sprite(self, pid):
		entity = self._all_entity.get(pid, None)
		if entity is not None:
			for e in entity.aoi_list:
				self.on_leave(e, entity.owner)
			del self._all_entity[pid]	
		
	def update(self, pid, old_xpos, old_ypos):
		entity = self._all_entity.get(pid, None)
		if entity is None:
			return
		x_move, y_move = False, False
		if entity.owner.xpos != old_xpos:
			x_move = True
			x_idx, x_aoi = self.check_in_x(entity.owner)	
		if entity.owner.ypos != old_ypos:
			y_move = True
			y_idx, y_aoi = self.check_in_y(entity.owner)
		
		if not (x_move or y_move):
			return
		new_aoi = []
		if x_move and y_move:
			for e in x_aoi:
				if e in y_aoi and e.pid != pid:
					new_aoi.append(e)
		elif x_move:
			y_left = entity.owner.ypos - entity.owner.aoi_radius
			y_right = entity.owner.ypos + entity.owner.aoi_radius
			for e in x_aoi:
				if y_left <= e.ypos <= y_right and e.pid != pid:
					new_aoi.append(e)
		elif y_move:
			x_left = entity.owner.xpos - entity.owner.aoi_radius
			x_right = entity.owner.xpos + entity.owner.aoi_radius
			for e in y_aoi:
				if x_left <= e.xpos <= x_right and e.pid != pid:
					new_aoi.append(e)
		if x_move:
			if entity.x_index < x_idx:
				self.x_list[entity.x_index : x_idx] = self.x_list[entity.x_index+1 : x_idx+1]
			else:
				self.x_list[x_idx+1 : entity.x_index+1] = self.x_list[x_idx : entity.x_index]
			self.x_list[x_idx] = entity.owner
			entity.x_index = x_idx
		if y_move:
			if entity.y_index < y_idx:
				self.y_list[entity.y_index : y_idx] = self.y_list[entity.y_index+1 : y_idx+1]
			else:
				self.y_list[y_idx+1 : entity.y_index+1] = self.y_list[y_idx : entity.y_index]
			self.y_list[y_idx] = entity.owner
			entity.y_index = y_idx
			
		# new enter	
		for e in new_aoi:
			if e not in entity.aoi_list:
				self.on_enter(entity.owner, e)
				self.on_enter(e, entity.owner)		
		# new leave
		for e in entity.aoi_list:
			if e not in new_aoi:
				self.on_leave(entity.owner, e)
				self.on_leave(e, entity.owner)
		entity.aoi_list = new_aoi		
				
	def check_in_x(self, sprite):
		x_left = sprite.xpos - sprite.aoi_radius
		x_right = sprite.xpos + sprite.aoi_radius
		x_range = [0, 0]
		enter_aoi = False
		x_idx = 0
		for idx, s in enumerate(self.x_list):
			if s.xpos < sprite.xpos:
				x_idx = idx
			if not enter_aoi:
				if s.xpos >= x_left:
					enter_aoi = True
					x_range[0] = idx
					x_range[1] = idx+1
			else:
				if s.xpos > x_right:
					enter_aoi = False
					x_range[1] = idx+1
					break
		x_aoi = self.x_list[x_range[0] : x_range[1]]
		return x_idx, x_aoi
		
	def check_in_y(self, sprite):
		y_left = sprite.ypos - sprite.aoi_radius
		y_right = sprite.ypos + sprite.aoi_radius
		y_range = [0, 0]
		enter_aoi = False
		y_idx = 0
		for idx, s in enumerate(self.y_list):
			if s.ypos < sprite.ypos:
				y_idx = idx
			if not enter_aoi:
				if s.ypos >= y_left:
					enter_aoi = True
					y_range[0] = idx
					y_range[1] = idx+1
			else:
				if s.ypos > y_right:
					enter_aoi = False
					y_range[1] = idx+1
					break
		y_aoi = self.y_list[y_range[0] : y_range[1]]
		return y_idx, y_aoi
	
	def on_enter(self, master, guest):
		msg_to_send = events.msg_sc_aoi_enter(pid=guest.pid, owner=guest.owner_side,
				roletype=guest.roletype, posx=guest.xpos, posy=guest.ypos, 
				posdir=guest.dir)
		player = s_global.main_ins.player_mgr.get_player(master.owner_side)	
		if player is not None:
			s_global.sender.send(player.sock, msg_to_send)
		
	def on_leave(self, master, guest):
		msg_to_send = events.msg_sc_aoi_leave(pid=guest.pid)
		player = s_global.main_ins.player_mgr.get_player(master.owner_side)		
		if player is not None:
			s_global.sender.send(player.sock, msg_to_send)
		
		
		
		
		
		